from __future__ import annotations

import re
from typing import List, Optional
import html

from .exceptions import InvalidEmailError, PayloadValidationError


EMAIL_PATTERN = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
MAX_PAYLOAD_SIZE = 256 * 1024  # 256KB
MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024  # 10MB per attachment
ALLOWED_MIME_TYPES = {
    "application/pdf",
    "application/zip",
    "application/msword",
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    "application/vnd.ms-excel",
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    "text/plain",
    "text/csv",
    "image/jpeg",
    "image/png",
    "image/gif",
}


def validate_email(email: str) -> bool:
    """Validate email address format"""
    if not email or not isinstance(email, str):
        return False
    return EMAIL_PATTERN.match(email.strip()) is not None


def validate_email_list(emails: List[str], field_name: str = "email") -> None:
    """Validate list of email addresses"""
    if not emails:
        raise InvalidEmailError(f"{field_name} list cannot be empty")

    for email in emails:
        if not validate_email(email):
            raise InvalidEmailError(f"Invalid email address in {field_name}: {email}")


def sanitize_html(html_content: str) -> str:
    """Basic HTML sanitization to prevent XSS"""
    if not html_content:
        return html_content

    # Remove dangerous patterns BEFORE escaping
    dangerous_patterns = [
        r'<script[^>]*>.*?</script>',  # Remove script tags
        r'on\w+\s*=\s*["\'][^"\']*["\']',  # Remove event handlers
        r'on\w+\s*=\s*\w+',  # Remove unquoted event handlers
        r'javascript:',  # Remove javascript: protocol
    ]

    sanitized = html_content
    for pattern in dangerous_patterns:
        sanitized = re.sub(pattern, '', sanitized, flags=re.IGNORECASE | re.DOTALL)

    # Then escape remaining HTML
    sanitized = html.escape(sanitized, quote=True)

    return sanitized


def validate_payload(payload: dict) -> None:
    """Validate email payload structure and content"""

    # Check required fields
    if "subject" not in payload:
        raise PayloadValidationError("Missing required field: subject")

    if "to" not in payload or not payload["to"]:
        raise PayloadValidationError("Missing required field: to")

    # Validate email addresses
    validate_email_list(payload["to"], "to")

    if "cc" in payload and payload["cc"]:
        validate_email_list(payload["cc"], "cc")

    if "bcc" in payload and payload["bcc"]:
        validate_email_list(payload["bcc"], "bcc")

    # Check body content
    body_text = payload.get("body_text")
    body_html = payload.get("body_html")

    if not body_text and not body_html:
        raise PayloadValidationError("At least one of body_text or body_html is required")

    # Validate attachments
    if "attachments" in payload and payload["attachments"]:
        validate_attachments(payload["attachments"])

    # Check total payload size
    import json
    payload_size = len(json.dumps(payload, ensure_ascii=False).encode('utf-8'))
    if payload_size > MAX_PAYLOAD_SIZE:
        raise PayloadValidationError(
            f"Payload size {payload_size} bytes exceeds maximum {MAX_PAYLOAD_SIZE} bytes"
        )


def validate_attachments(attachments: List[dict]) -> None:
    """Validate email attachments"""

    for idx, att in enumerate(attachments):
        if not isinstance(att, dict):
            raise PayloadValidationError(f"Attachment {idx} must be a dictionary")

        # Check required fields
        if "filename" not in att:
            raise PayloadValidationError(f"Attachment {idx} missing filename")

        if "content_bytes" not in att:
            raise PayloadValidationError(f"Attachment {idx} missing content_bytes")

        # Validate MIME type
        mime_type = att.get("mime_type", "application/octet-stream")
        if mime_type not in ALLOWED_MIME_TYPES:
            raise PayloadValidationError(
                f"Attachment {idx} has disallowed MIME type: {mime_type}"
            )

        # Check size
        content = att.get("content_bytes", b"")
        if isinstance(content, bytes):
            size = len(content)
        else:
            size = len(str(content))

        if size > MAX_ATTACHMENT_SIZE:
            raise PayloadValidationError(
                f"Attachment {idx} size {size} bytes exceeds maximum {MAX_ATTACHMENT_SIZE} bytes"
            )
